//拷贝原来的代码一份来做。
//真正的开发中，我们可以用git来管理
const http = require('http');
const url = require('url');
const fs = require('fs');
const qs = require('querystring');
const path = require('path');
const md5 = require("md5")
//明了登录的原理：https://linux.cn/article-9367-1.html

//登录和会话要使用cookie
//查阅一下资料，自己选用一个cookies处理模块.//https://www.npmjs.com/package/cookies 
//百度时经常看到express,koa，暂时用不着
//为了更能说明原理，我暂时没有使用。

const hostname = "10.101.96.3";
const port = 8000;
let show = {
    1126001: {
        id: "1126001",
        number: 1,
        img: "../imgs/shangping_1.jpg",
        title: "Jeep吉普男鞋2020春夏透气帆布鞋高低帮潮流百搭马丁靴男鞋女鞋潮",
        price: 238.0,
        sellNumber: "1.5万+",
        comment: 48392,
        integral: 29
    },
    1126002: {
        id: "1126002",
        number: 1,
        img: "../imgs/shangping_2.jpg",
        title: "tanmesso胸包男韩版斜挎包休闲牛津布小包背包单肩包男包胸前包潮",
        price: 58.0,
        sellNumber: "1.7万+",
        comment: 485322,
        integral: 42
    },
    1126003: {
        id: "1126003",
        number: 1,
        img: "../imgs/shangping_3.jpg",
        title: "回力男鞋秋季2020新款高帮登山鞋徒步运动鞋男防水防滑户外休闲鞋",
        price: 118.0,
        sellNumber: "1.2万+",
        comment: 63367,
        integral: 39
    },
    1126004: {
        id: "1126004",
        number: 1,
        img: "../imgs/shangping_4.png",
        title: "三只松鼠芒果干60g蜜饯果脯水果干网红休闲办公室零食小吃食品",
        price: 11.0,
        sellNumber: "1.3万+",
        comment: 64753,
        integral: 32
    },
    1126005: {
        id: "1126005",
        number: 1,
        img: "../imgs/shangping_5.jpg",
        title: "小才天4G全网通儿童电话手表防水学生智能可视频通话wifi防摔多功能定位可插卡拍照女孩适用小米华为360手机",
        price: 118.0,
        sellNumber: "1.9万+",
        comment: 8592,
        integral: 53
    },
    1126006: {
        id: 1126006,
        number: 1,
        img: "../imgs/shangping_6.png",
        title: "奥克斯取暖器电暖风机家用电暖气小太阳热风机办公室节能省电小型",
        price: 59.9,
        sellNumber: "1.7万+",
        comment: 65425,
        integral: 42
    },
    1126007: {
        id: "1126007",
        number: 1,
        img: "../imgs/shangping_7.jpg",
        title: "Crocs卡骆驰男鞋女童鞋2020秋季运动鞋拖鞋沙滩鞋凉鞋防滑洞洞鞋",
        price: 209.0,
        sellNumber: "1.6万+",
        comment: 22567,
        integral: 27
    },
    1126008: {
        id: "1126008",
        number: 1,
        img: "../imgs/shangping_8.jpg",
        title: "英国Limit空军飞行员手表男士夜光商务运动潮流粗犷正品名牌学生",
        price: 498.0,
        sellNumber: "1.8万+",
        comment: 6222,
        integral: 32
    },
    1126009: {
        id: "1126009",
        number: 1,
        img: "../imgs/shangping_9.jpg",
        title: "纽曼头戴式无线耳麦可调频无线话筒麦旅游景点导游讲解用扩音器无线麦教学K歌直播定频无线耳机老师上课麦克",
        price: 149.0,
        sellNumber: "0.5万+",
        comment: 7542,
        integral: 19
    },
    1126010: {
        id: "1126010",
        number: 1,
        img: "../imgs/shangping_10.jpg",
        title: "电压力锅家用双胆迷你小型4L5L6L8L升智能高压电饭煲电高压锅",
        price: 119.9,
        sellNumber: "2.7万+",
        comment: 31245,
        integral: 14
    },
    1126011: {
        id: "1126011",
        number: 1,
        img: "../imgs/shangping_11.jpg",
        title: "折叠躺椅午休午睡床阳台休闲沙滩靠背椅懒人沙发便携靠睡椅子家用",
        price: 69.0,
        sellNumber: "1.2万+",
        comment: 33242,
        integral: 21
    },
    1126012: {
        id: "1126012",
        number: 1,
        img: "../imgs/shangping_12.png",
        title: "【双11现货】兰蔻大粉水新清滢柔肤水400ml 深层补水保湿舒缓肌肤",
        price: 435.0,
        sellNumber: "1.2万+",
        comment: 8593,
        integral: 55
    },
    1126013: {
        id: "1126013",
        number: 1,
        img: "../imgs/shangping_13.png",
        title: "【双11抢先加购】美宝莲fitme乳液粉底液干皮救星遮瑕保湿持久",
        price: 139.0,
        sellNumber: "1.4万+",
        comment: 18022,
        integral: 53,
    },
    1126014: {
        id: "1126014",
        number: 1,
        img: "../imgs/shangping_14.png",
        title: "奥克斯足浴盆电动按摩加热洗脚盆家用全自动恒温泡脚桶神器高深桶",
        price: 138.0,
        sellNumber: 5424,
        comment: 485322,
        integral: 22
    },
    1126015: {
        id: "1126015",
        number: 1,
        img: "../imgs/shangping_15.png",
        title: "维达抽纸超韧3层M码120抽24包卫生餐巾纸巾抽纸家用实惠整箱装",
        price: 89.9,
        sellNumber: "2.1万+",
        comment: 53642,
        integral: 23
    },
};


// let cart = [];
//在内存中保存购物车内容，当然也可以放入文件，需要的时候读文件，也可以放入数据库，需要的时候读数据库。
//准备简单地以Map来记录用户信息，请查阅类Map用法。
//简单地说：map就是一个key-value键值对列表。
let users = new Map(); //key=username, value=object {sessionid,个人信息类, 购物车类}
/*
    users.set('lyshsd', {sessionid:'B1E7C4C2009E322321374B5AEB03E383',个人信息:{name:"lyshsd",pwdMD5:"e526d1316d8f7cf97ec8b102be156c31",gender:"male"},   购物车信息: [{ id: 1, name: "手机游戏神器", 数量: 1, price: 2 }, { id: 2, name: "华为手机Mate40", 数量: 1, price: 8000 }]})
    //todo:查阅资料，看看怎么生成sessionid，也可以查查什么是md5

*/

//为了简化，我们让ajax中，post发送购物车数据，get取购物车数据
const server = http.createServer((req, res) => {
    // console.log("新到请求：", req.url)
    let datas = "";
    req.on('error', (err) => {
        console.error(err);
    }).on('data', (chunk) => {
        datas += chunk;
    }).on('end', () => {
        res.statusCode = 200; //预设最终会成功
        res.setHeader('Content-Type', 'text/html;charset=UTF-8'); //预设是text/html
        res.setHeader("Access-Control-Allow-Origin", "*"); //允许跨域

        //用最新的方法处理url，详见“探索url处理.js”，更详见请参见官网url模块
        let curl = new url.URL(req.url, `http://${hostname}:${port}`);
        if (req.method === "POST") {
            //暂时没啥可做，先空着.数据已在datas中了。
        } else if (req.method === "GET") {
            //get数据在此datas放着
            datas = curl['searchParams'].toString()
        } else {
            console.log(req.method, ":这个方法的处理暂未考虑。");
            pipe404(res);
            return;

        }
        // console.log("href:", curl.href);

        //注意，内容发送，要在协议头发送之后。若已有pipe(res)，就不要再用res.end()
        //以下路由处理。大都用的同步，这里可能并不太好，但容易理解。一种改进的方法可能是：使用事件触发EventEmitter

        switch (curl.pathname) {
            case "/":
                //访问首页
                req.url = "/html/index.html";
                handleAsFile(req, res, req.url)
                break;
            case "/favicon.ico":
                req.url = "/../shopCart/src/imgs/favicon.ico";
                handleAsFile(req, res, req.url);
                break;

            case "/html/product_details.html":
                //把传过来的商品id放进cookie
                res.setHeader('Set-Cookie', `${datas}`);
                //返回详情页模板页面detail.html
                handleAsFile(req, res, curl.pathname);
                break;
            case "/getSKU":
                res.statusCode = 200;
                let dataId = datas.split('=')[1]
                // console.log(show[dataId])
                if (show.hasOwnProperty(dataId)) {
                    res.end(`${JSON.stringify(show[dataId])}`)
                }
                break;

            case "/showCart": //保存购物车信息
                //todo:补全代码，只有登录了的用户，才能送信息进来保存
                let datas1 = JSON.parse(datas);
                if (LoginCookies().sessionid != undefined) {
                    //在这里，客户端送购物车信息到服务器保存。
                    // cart.push(datas1);
                    //加入购物车，不同用户加入的不一样，一一对应
                    (users.get(LoginCookies().username).cartInfo).push(datas1)
                    //调用合并id的函数
                    let Cart1 = getResultCart();
                    //重新赋值给购物车
                    (users.get(LoginCookies().username).cartInfo) = [...Cart1]
                    console.log(users)
                }
                res.statusCode = 200;
                res.end();
                //返回,不然客户端等不到返回信息，出错。
                // console.log(cart)
                break;
            case "/getCart":
                //todo:补全代码，只有登录了的用户，才能取购物车信息
                //调用合并id的函数
                let Cart = getResultCart();
                res.writeHead(200, {
                    'Content-Type': 'text/json:charset=UTF-8'
                });
                if (LoginCookies().sessionid != undefined) {
                    res.end(JSON.stringify(Cart));
                }
                break;
            case "/updateCart":
                //调用合并id的函数
                // let Cart = getResultCart();
                res.writeHead(200, {
                    'Content-Type': 'text/json:charset=UTF-8'
                });
                // console.log(JSON.parse(datas))
                //拷贝
                users.get(LoginCookies().username).cartInfo = [...JSON.parse(datas)]
                // console.log(cart)
                res.end(JSON.stringify(datas));
                break;
            case "/reg":
                //把自己登记在名单内，下次登录时，可以在名单中找到你。我们把名单放在一个map中。
                let userReg = JSON.parse(datas); //先前后端约好格式，应和后端的个人信息对应
                userReg.pwdMD5 = md5(userReg.pwdMD5);
                let sessionId = md5(userReg.name + (new Date()).getTime()).toUpperCase()
                if (users.has(userReg.name)) {
                    res.writeHead(200, {
                        'Content-Type': 'text/json:charset=UTF-8'
                    });
                    res.end('{"code":"4","msg":"用户已存在，请去登录"}'); //注意，先约定前后端交互的信息格式。code表示自定义的状态码（后端定义好），4表示注册不成功
                } else {
                    //users.set('lyshsd', {sessionid:'B1E7C4C2009E322321374B5AEB03E383',userInfo:{name:"lyshsd",pwdMD5:"e526d1316d8f7cf97ec8b102be156c31",gender:"male"},   购物车信息: [{ id: 1, name: "手机游戏神器", 数量: 1, price: 2 }, { id: 2, name: "华为手机Mate40", 数量: 1, price: 8000 }]})
                    users.set(userReg.name, {
                        sessionid: sessionId,
                        userInfo: userReg,
                        cartInfo: []
                    });
                    res.writeHead(200, {
                        'Content-Type': 'text/json:charset=UTF-8',
                    });
                    res.end('{"code":"2","msg":"注册成功,请去登录"}'); //注意，先约定前后端交互的信息格式。code表示自定义的状态码（后端定义好），4表示注册不成功
                }
                break;

            case "/login":
                //验证用户名在不在用户列表中，密码对不对得上。
                //有这个人，密码又OK，那就让其通过登录，然后生成一个sessionid，写入客户端cookie，同时记在服务器，存活期是浏览器打开的时间，开启一段会话
                //思考：如何让用户保持登录1个月，或者无限期？
                let userLogin = JSON.parse(datas); //qstr='{"name":"lyshsd","pwdMD5":"e526d1316d8f7cf97ec8b102be156c31"}'.先前后端约好格式
                //一般不记录密码的明码，而是记录密码明码的md5
                userLogin.pwdMD5 = md5(userLogin.pwdMD5);
                let sessionID = md5(userLogin.name + (new Date()).getTime()).toUpperCase()
                if (users.has(userLogin.name) && users.get(userLogin.name).userInfo.pwdMD5 === userLogin.pwdMD5) {
                    let exp = new Date()
                    exp.setTime(exp.getTime() + 24 * 60 * 60 * 1000)
                    res.writeHead(200, {
                        'Content-Type': 'text/json:charset=UTF-8',
                        //sessionid的命名可以随意后端定，不一定是sessonid；值要按一定的算法生成，起码保证用户之间不重复
                        // 'Set-Cookie': 'username=' + userLogin.name,
                        'Set-Cookie': ['sessionid=' + sessionID + ';expires=' + exp.toGMTString(), 'username=' + userLogin.name + ';expires=' + exp.toGMTString()]
                    });
                    //res.cookie('sessionid', 'B1E7C4C2009E322321374B5AEB03E383'})
                    //在服务器端备案
                    users.get(userLogin.name).sessionid = sessionID;
                    res.end(JSON.stringify({
                        "code": 2,
                        "msg": users.get(userLogin.name)
                    })); //把整个信息发往前端


                } else {
                    if (users.has(userLogin.name)) {
                        res.writeHead(200, {
                            'Content-Type': 'text/json:charset=UTF-8',
                        });
                        res.end(JSON.stringify({
                            "code": 1,
                            "msg": '密码不正确'
                        }));
                        //todo补全代码:返回约定的代码和消息，比如：code：1，msg:密码不对
                    } else {
                        //todo补全代码：返回约定的代码消息，比如：code:5, msg:用户不存在
                        res.writeHead(200, {
                            'Content-Type': 'text/json:charset=UTF-8',
                        });
                        res.end(JSON.stringify({
                            "code": 5,
                            "msg": '用户名不存在'
                        }));
                    }
                }
                break;
            case "/logout":
                //什么是登出？就是结束会话。怎么结束会话？删除代表会话的cookie：sessionid，同时服务器内去除相关记录。
                let out = JSON.parse(datas);
                //todo补全代码:删除sessionid=B1E7C4C2009E322321374B5AEB03E383的用户的sessionid,或者置其为0
                res.writeHead(200, {
                    'Content-Type': 'text/json:charset=UTF-8',
                    //删除客户端的sessionid，关键是Max-Age=0
                    'Set-Cookie': ['sessionid=' + users.get(out).sessionid + ';Max-Age=0', 'username=' + out.name + ';Max-Age=0']
                });

                res.end('成功登出');
                break;
            default:
                handleAsFile(req, res, curl.pathname);
                break;
        }
        //合并相同id的数量
        function getResultCart() {
            let result = {};
            for (let i = 0, len = (users.get(LoginCookies().username).cartInfo).length; i < len; i++) {
                let dataArr = (users.get(LoginCookies().username).cartInfo)[i];
                if (result[dataArr.id]) {
                    result[dataArr.id].number++
                    // console.log(result[dataArr.id])
                } else {
                    result[dataArr.id] = {
                        ...dataArr,
                    };
                }
            }
            let resultCart = []; //合并后的数组
            for (const key in result) {
                resultCart.push(result[key]);
            }
            return resultCart;
        }

        //判断是否出在登录状态
        function LoginCookies() {
            let Cookies = {}
            req.headers.cookie && req.headers.cookie.split(';').forEach((Cookie) => {
                if (!Cookie) {
                    return;
                }
                let parts = Cookie.split('=');
                Cookies[parts[0].trim()] = (parts[1] || '').trim()
            });
            return Cookies;
        }
    }) //end响应函数


})
server.listen(port, hostname, () => {
    console.log(`服务器运行在 http://${hostname}:${port}/`);
})



function handleAsFile(req, res, pathname) {
    //todo:代码补全：假如这是一个授权才能访问的文件，你可以先看req cookie中有无sessionid，而且值和服务器端记录的一样，
    //不然的话，则跳转到登录页面。

    //如果请求网页文件时没有登录，先登录
    // if (path.extname(pathname) == '.html' && (!req.headers.cookie || req.headers.cookie.indexOf('sessionid=B1E7C4C2009E322321374B5AEB03E383') < 0)) {
    //     pathname = "/p06login.html";
    // }

    let f = __dirname + '/../shopCart/src' + pathname;
    //详情参阅官方文档

    fs.stat(f, (err, stat) => {
        if (err) {
            console.log(err);
            pipe404(res);
        } else {
            if (stat.isFile()) {
                setContentType(res, path.extname(pathname));
                fs.createReadStream(f).pipe(res);
            } else {
                pipe404(res);
            }
        }
    });
}

function getContentType(ext) {
    //建一个类，key-value,key是扩展名，值是content-type. 参考：https://tool.oschina.net/commons
    const extTypes = {
        ".html": "text/html",
        ".js": 'application/x-javascript',
        ".json": 'application/json', //https://tool.oschina.net/commons竟然没有这一项，我自己加上去的
        ".*": 'application/octet-stream', //对应没有扩展名的文件
        ".css": "text/css",
        ".png": "image/png",
        ".jpg": "application/x-jpg",
        ".jpeg": "image/jpeg",
        ".ico": "application/x-ico",
        ".gif": "image/gif"
        //各种图片，js等。完整的话，应该把整个放进来。
    }

    //试试通过req.url取扩展名:path.extName(req.url);
    //let ext = path.extname(req.url);
    if (ext == '') ext = ".*";
    let type = extTypes[ext];

    if (!type) {
        console.log(ext, "扩展名对应的数据类型未查到！");
        1
        type = 'application/octet-stream';
    }
    return type;
}

function setContentType(res, ext) {
    res.setHeader('Content-Type', getContentType(ext));
}

function pipe404(res) {
    res.statusCode = 404;
    res.setHeader('Content-Type', 'text/html;charset=UTF-8');
    res.end("<h1 style='color:red;'>404:请求内容不存在或服务暂不支持！</h1>");
}